package com.segersten.othello.service.akka;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;
import akka.dispatch.Await;
import akka.dispatch.Future;
import akka.pattern.Patterns;
import akka.util.Duration;
import akka.util.Timeout;

import com.segersten.othello.service.akka.game.CreateBoard;
import com.segersten.othello.service.akka.game.OthelloBoardServiceActorFactory;
import com.segersten.othello.service.akka.lobby.LobbyServiceActor;

public class AkkaUtil implements GameObjectPool {
	
	private static final String OTHELLO_SYSTEM = "othello";
	
	private static String ACTOR_PATH_TEMPLATE = String.format("akka://%s/user/", OTHELLO_SYSTEM);

	private static Timeout ASK_TIMEOUT = null;
	
	private static ActorSystem system;

	public static void channelTell(UntypedActor actor, Object message) {
		actor.getContext().sender().tell(message);
	}

	@Override
    public GameObjectReference actorFor(String reference) {
		String path = ACTOR_PATH_TEMPLATE + reference;
		ActorRef actor = getSystem().actorFor(path);
		if (actor == null) {
			throw new NullPointerException(String.format("No actor found for '%s'", reference));
		}
		return new GameObjectReference(actor);
	}

	@Override
    public GameObjectReference createBoardServiceActor(String reference) {
		ActorRef actor = getSystem().actorOf(new Props(new OthelloBoardServiceActorFactory(this, reference)), reference);
		return new GameObjectReference(actor);
	}

	@Override
    public String getReference(GameObjectReference ref) {
        ActorRef actor = (ActorRef) ref.getId();
        return actor .path().toString().replace(ACTOR_PATH_TEMPLATE,"");
    }
    
    @Override
    public void tell(GameObjectReference ref, Object message)
    {
        ActorRef actor = (ActorRef) ref.getId();
        actor.tell(message);
    }
    
	@Override
    public Object ask(GameObjectReference ref, Object message) {
		try {
			ActorRef actorRef = (ActorRef) ref.getId();
            Future<Object> future = Patterns.ask(actorRef , message, getAskTimeout());
			return Await.result(future, getAskTimeout().duration());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private Timeout getAskTimeout() {
		if (ASK_TIMEOUT==null) {
			ASK_TIMEOUT = Timeout.durationToTimeout(Duration.parse("15 seconds"));
		}
		return ASK_TIMEOUT;
	}
	
	public void initSystem(UntypedActorFactory lobbyServiceActorFactory) {
        system = ActorSystem.create(OTHELLO_SYSTEM);
        ActorRef lobbyActor = getSystem().actorOf(new Props(lobbyServiceActorFactory), LobbyServiceActor.SERVICE_NAME);
    }

	public void shutdown() {
		getSystem().shutdown();
	}
	
	private ActorSystem getSystem() {
        if (system==null) {
            throw new NullPointerException("System not initialized");
        }
        return system;
    }

}
